
#include "Font.h"
#include "globals.h"
#include "Image.h"
#include "MouseHelper.h"
#include "sprite.h"
#include "Animation.h"
#include "Entity.h"
#include "2dmap.h"

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <time.h>



using namespace std;

Vector2 MousePositionDiff(Vector2 checkedVector);
bool Init();
void Update(int delta);
void Draw();
void Cleanup();
bool ToggleBool(bool input);


//Sprite *mySprite = NULL;
//Image *twiRun = NULL;
//Animation *myAnimation = NULL;
//Entity *pTwilightEntity = NULL;
//Image *pCatacombsSheet = NULL;
//Animation *pFloorSprite = NULL;
TwoDMap * pCurrentMap = NULL;



int main(int argc, char* argv[])
{
    // If we failed to initialize, we should just bail.
    if (Init() == false)
    {
        return 1;
    }

    double now = -1.0f; // Used to temporarily store the current time, for timing-related calculations.
    double lastSecond = 0; // Updated once per second, used to keep track of how long a second actually is. (If now>=(lastSecond+1000), new second.) Used for FPS.
    Uint32 frame = 0; // Keeps track of the number of frames rendered during the current second. Used for FPS calculation later.
    double remainder = 0.0f; // Keeps track of amount of milliseconds lost to whole number rounding, to ensure a smooth and perfectly-accurate framerate.

    // Define event handler. Used later to poll the event queue.
    SDL_Event event;

    // We'll keep track of the state of the mouse; if any of these change,
    // then we need to inform the mouse helper of that fact.
    bool isLeftMouseButtonDown = false;
    int mouseX = -1;
    int mouseY = -1;
    bool drawCursor = false;

    gIsQuitting = false;

    while (!gIsQuitting)
    {
        int delta = 0;
        double oldNow = now;

        now = (double)SDL_GetTicks();

        // First we calculate how much time has elapsed since the last time through this loop -
        // this is the value we'll pass into the update pass.
        if (oldNow >= 0)
        {
            delta = (int)(now - oldNow);
        }

        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
                case SDL_WINDOWEVENT:
                    if (event.window.event == SDL_WINDOWEVENT_ENTER)
                    {
                        drawCursor = true;
                        isLeftMouseButtonDown = (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LMASK);
                    }
                    else if (event.window.event == SDL_WINDOWEVENT_LEAVE)
                    {
                        drawCursor = false;
                        isLeftMouseButtonDown = false;
                    }

                    MouseHelper::UpdateState(isLeftMouseButtonDown, mouseX, mouseY, drawCursor);
                    break;

                case SDL_MOUSEMOTION:
                    // The mouse has moved; we need to notify the mouse helper.
                    mouseX = event.motion.x;
                    mouseY = event.motion.y;

                    MouseHelper::UpdateState(isLeftMouseButtonDown, mouseX, mouseY, drawCursor);
                    break;

                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP:
                    // A mouse button has changed; if it was the left mouse button,
                    // then we need to notify the mouse helper.
                    if (event.button.button == SDL_BUTTON_LEFT)
                    {
                        isLeftMouseButtonDown = event.button.state == SDL_PRESSED;
                    }

                    MouseHelper::UpdateState(isLeftMouseButtonDown, mouseX, mouseY, drawCursor);
                    break;

                case SDL_QUIT:
                    // Someone tried to close the window, or otherwise requested the task to end.
                    // Flip the exit switch, which will be obeyed when this frame finishes rendering.
                    gIsQuitting = true;
                    break;
                case SDL_KEYDOWN:
                    keyPressed = event.key.keysym.sym;
            }
        }

        Update(delta);

        // Blank the screen before drawing the new frame.
        SDL_SetRenderDrawColor(gpRenderer, 0, 0, 0, 255);
        SDL_RenderClear(gpRenderer);

        Draw();

        // Swap the double buffer to display the new frame.
        SDL_RenderPresent(gpRenderer);

        // Handle FPS calculation every second. (Only in Debug build target, with MLI_DEBUG_NO_FPS not defined.)
        if (now - 1000 >= lastSecond)
        {
            if (lastSecond > 0)
            {
                #ifdef DEBUG
                cout << "FPS: " << frame << endl;
                #endif

                frame = 0;
            }

            lastSecond = now;
        }

        // Increment the frame counter (for FPS).
        frame++;

        // Calculate how long it took to execute all of the above, to compensate for the additional delay for framerate regulation.
        double executionTime = (double)SDL_GetTicks() - now;

        // If we should wait to enforce the desired framerate, do so now.
        if (gFramerate > 0.0 && executionTime < 1000.0 / gFramerate)
        {
            double wait = 1000.0 / gFramerate - executionTime;
            remainder += fmod(wait, 1.0);

            if (remainder >= 1.0)
            {
                wait += remainder - fmod(remainder, 1.0);
                remainder = fmod(remainder, 1.0);
            }

            SDL_Delay((int)wait);
        }
        else
        {
            // Always wait at least 1 ms - this defers to other threads as needed.
            SDL_Delay(1);
        }
    }

    Cleanup();
    return 0;
}

bool Init()
{
    // Seed the pseudo-random number generator.
    srand((unsigned int)time(NULL));

    gScreenWidth = 1000;
    gScreenHeight = 700;
    gFramerate = 60.0; // FPS (0 for unlimited)

    gTitle = "Messing about";

    // Turn on ALL THE THINGS.
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
    {
        return false;
    }

    atexit(SDL_Quit);

    gpWindow = SDL_CreateWindow(gTitle.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, gScreenWidth, gScreenHeight, SDL_WINDOW_SHOWN);

    // If we couldn't get a window, we're pretty screwed.
    if (gpWindow == NULL)
    {
        return false;
    }

    gpRenderer = SDL_CreateRenderer(gpWindow, -1, SDL_RENDERER_ACCELERATED);

    // Ditto for the renderer.
    if (gpRenderer == NULL)
    {
        return false;
    }

    TTF_Init();

    MouseHelper::Init();

    pCurrentMap = new TwoDMap();


    return true;
}

void Update(int delta)
{
    static int mouseDownStartTime = 0;
    static bool wasMouseDown = false;
    static Vector2 lastMousePosition(0, 0);

    //myAnimation->Update(delta);
    //pFloorSprite->Update(delta);
    pCurrentMap->SetScreenDrawArea(RectangleWH(40, 40, 400, 400));
    pCurrentMap->Update(delta);

}

void Draw()
{
    pCurrentMap->Draw();

}

void Cleanup()
{
    // Close all open fonts and shut down the font thread.
    TTF_Quit();

    // Free the renderer and window.
    if (gpRenderer != NULL)
    {
        SDL_DestroyRenderer(gpRenderer);
        gpRenderer = NULL;
    }

    if (gpWindow != NULL)
    {
        SDL_DestroyWindow(gpWindow);
        gpWindow = NULL;
    }
}

bool ToggleBool(bool input)
{
    if (input) {return false;}
    else {return true;}
}

Vector2 MousePositionDiff(Vector2 checkedVector)
{
    return Vector2(MouseHelper::GetMousePosition().GetX() - checkedVector.GetX(),
                   MouseHelper::GetMousePosition().GetY() - checkedVector.GetY());
}

